from django.db import transaction
from rest_framework import serializers

from apps.goods.models import SKU,GoodsCategory, SPU,SPUSpecification,SpecificationOption, SKUSpecification
from celery_tasks.html.tasks import generate_static_sku_detail_html


class SKUSpecificationSerializer(serializers.ModelSerializer):
    """
    SKU具体规格信息
    """
    spec_id = serializers.IntegerField()
    option_id = serializers.IntegerField()

    class Meta:
        model = SKUSpecification
        fields = ("spec_id", "option_id")


class SKUKSerializer(serializers.ModelSerializer):
    """
    SKU序列化器
    """

    # 前端不会传递spu和category,默认返回下划线id的形式说明不参与反序列化
    # read_only,就不在对这两个字段进行验证了
    # spu 和 category 不需要参与保存和更新
    spu = serializers.StringRelatedField(read_only=True)
    category = serializers.StringRelatedField(read_only=True)
    # 前端传递spu_id 和 category_id
    spu_id = serializers.IntegerField()
    category_id = serializers.IntegerField()

    specs = SKUSpecificationSerializer(many=True)

    class Meta:

        model = SKU
        fields = "__all__"

    # 规格表在specs字段中,需要定义出来之后,在重写create来保存specs
    def create(self, validated_data):
        # 删除方法一:
        # 1. 获取specs数据
        # specs = validated_data.get("specs")
        # 2. 将specs删除
        # del validated_data["specs"]
        # 删除方法二:
        specs = validated_data.pop("specs")

        # 开启事务
        with transaction.atomic():
            # 设置保存点
            save_point = transaction.savepoint()
            try:
                # 3. 保存sku表数据
                # 保存方法1:
                # sku = super().create(validated_data)
                # 保存方法2:
                sku = SKU.objects.create(**validated_data)
                # 4. 保存sku具体规格表数据
                for spec in specs:
                    SKUSpecification.objects.create(sku=sku,
                                                    spec_id=spec.get("spec_id"),
                                                    option_id=spec.get("option_id")
                                                      )

                # 生成html静态页面
                # 生成静态页面时,需要自己增加一个图片,不然会报错.
                sku.default_image="group1/M00/00/02/wKi4jF5IyvuAbwxaAAUp6TbG7h82839667"
                sku.save()
                # delay: 延迟执行,可以先执行后面的代码
                generate_static_sku_detail_html.delay(sku.id)
            except:
                # 回滚
                transaction.savepoint_rollback(save_point)
                raise serializers.ValidationError("保存失败")
            else:
                # 提交
                transaction.savepoint_commit(save_point)
                return sku

    # 修改数据时,报500错
    # update()` method does not support writable nested fields by default.
    def update(self, instance, validated_data):
        """
        重写更新方法
        :param instance:
        :param validated_data:
        :return:
        """
        # 1. 删除掉cpecs
        specs = validated_data.pop("specs")

        # 开启事务
        with transaction.atomic():
            # 设置保存点
            save_point = transaction.savepoint()
            try:
                # 2. 调用父类的更新方法,对原有的数据进行更新
                super().update(instance,validated_data)
                # 3. 自己实现遍历,对specs进行更新
                for spec in specs:
                    SKUSpecification.objects.filter(
                        sku = instance,
                        spec_id=spec.get("spec_id")
                    ).update(
                        option_id=spec.get("option_id")
                    )
            except:
                # 回滚
                transaction.savepoint_rollback(save_point)
                raise serializers.ValidationError("更新失败")
            else:
                # 提交
                transaction.savepoint_commit(save_point)

                return instance



class ThreeCategorySerializer(serializers.ModelSerializer):
    """
    三级分类序列化器
    """

    class Meta:
        model = GoodsCategory
        fields = ["id","name"]


class SimpleSPUSerializer(serializers.ModelSerializer):
    """
    SPU序列化器
    """
    class Meta:
        model = SPU
        fields = ["id","name"]



class SpecificationSerializer(serializers.ModelSerializer):
    """
    规格选项序列化器
    因为要返回两个字段,单独使用primarykey和stringfield都不行,所以要单独定义序列化器,才能指定两个字段
    """
    class Meta:
        model = SpecificationOption
        fields = ["id","value"]


class SPUSpecsSerializer(serializers.ModelSerializer):
    """
    规格序列化器
    """
    # 关联嵌套返回,返回规格时,规格选项也要返回,父表嵌套子表返回
    options = SpecificationSerializer(many=True)

    # 将关联的spu数据返回 子表嵌套父表(名称一般只参与序列化返回)
    # spu = serializers.StringRelatedField(read_only=True)
    # id 有可能前端会传递id 信息进行保存所以不指定read_only=True
    # spu_id = serializers.IntegerField()

    class Meta:
        model = SPUSpecification
        fields = "__all__"
